package com.thoughtworks.impersonator.http.verify;

import java.io.File;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.log4j.Logger;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpRequestEncoder;
import org.jboss.netty.handler.codec.http.HttpResponseDecoder;

import com.thoughtworks.impersonator.http.message.HttpExchange;
import com.thoughtworks.impersonator.http.message.RawResponse;
import com.thoughtworks.impersonator.http.message.RequestIdentifier;
import com.thoughtworks.impersonator.http.message.storage.HttpExchangeSerialiser;
import com.thoughtworks.impersonator.http.message.storage.HttpExchangeStorage;
import com.thoughtworks.impersonator.http.message.transform.DefaultRequestTransformer;
import com.thoughtworks.impersonator.http.message.transform.DefaultResponseTransformer;
import com.thoughtworks.impersonator.http.message.transform.RequestTransformer;
import com.thoughtworks.impersonator.http.message.transform.ResponseTransformer;

public class Verifier {

    private static final Logger log = Logger.getLogger(Verifier.class);

    private final String targetHost;
    private final int targetPort;
    private final HttpExchangeStorage storage;

    public Verifier(String targetHost, int targetPort, File storageDir, RequestIdentifier requestIdentifier,
            RequestTransformer requestTransformer, ResponseTransformer responseTransformer) {
        HttpExchangeSerialiser serialiser = new HttpExchangeSerialiser(requestIdentifier, requestTransformer, responseTransformer);
        this.storage = new HttpExchangeStorage(storageDir, serialiser);
        this.targetHost = targetHost;
        this.targetPort = targetPort;
    }

    public Verifier(String server, int serverPort, File storageDir, RequestIdentifier requestIdentifier) {
        this(server, serverPort, storageDir, requestIdentifier, new DefaultRequestTransformer(),
                new DefaultResponseTransformer());
    }

    public boolean verify() throws Exception {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        ClientBootstrap cb = new ClientBootstrap(new NioClientSocketChannelFactory(threadPool, threadPool));
        ChannelPipeline pipeline = cb.getPipeline();
        pipeline.addLast("decoder", new HttpResponseDecoder());
        pipeline.addLast("chunkAggregator", new HttpChunkAggregator(1048576));
        pipeline.addLast("encoder", new HttpRequestEncoder());
        VerifyMessageHandler handler = new VerifyMessageHandler();
        pipeline.addLast("responseCollector", handler);

        boolean result = true;
        for (HttpExchange exchange : storage.all()) {
            ChannelFuture future = cb.connect(new InetSocketAddress(targetHost, targetPort));
            Channel channel = future.awaitUninterruptibly().getChannel();
            if (!future.isSuccess()) {
                throw new RuntimeException(future.getCause());
            }
            channel.write(exchange.request());
            channel.getCloseFuture().awaitUninterruptibly();
            RawResponse expectedResponse = new RawResponse(exchange.response());
            RawResponse actualResponse = new RawResponse(handler.getResponse());
            if (!expectedResponse.equals(actualResponse)) {
              result = false;
              log.debug(String.format("%s doesn't match. expected [%s] received [%s]", "uid", expectedResponse,
                      actualResponse));
          }
        }
        cb.releaseExternalResources();
        return result;
    }

}
